#pragma once

#include "Cond.hpp"
#include "Mutex.hpp"
#include <iostream> 
#include <pthread.h>
#include <unistd.h>
#include <queue>

// namespace BlockQueueModule
// {
//     static const int gcap = 10;
//     //用queue封装阻塞队列
//     template<typename T>
//     class BlockQueue
//     {
//     private:
//         bool IsFull(){return _cap == _q.size();}
//         bool IsEmpty(){return _q.empty();}
//     public:
//         //构造
//         BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
//         {
//         }
//         //析构
//         ~BlockQueue()
//         {
//         }
//                 //入队列，将数据放进队列中
//         void Equeue(const T& in)
//         {
//             //初始化锁
//             MutexModule::LockGuard lockguard(_mutex);
//             while(IsFull()) 
//             {
//                 std::cout<<"生产者进入等待"<<std::endl;
//                 _pwait_num++;//等待时++
//                 _productor_cond.wait(_mutex);
//                 _pwait_num--;//唤醒时--
//             }
//             _q.push(in); //生产，保证队列目前不为满
//             if(_cwait_num)
//             {
//                 //唤醒consumer
//                 _consumer_cond.Nodify();
//             }
//         }
//         //取出数据
//         void Pop(T *out)
//         {
//             MutexModule::LockGuard lockguard(_mutex);
//             while(IsEmpty())
//             {
//                 std::cout<<"消费者进入等待"<<std::endl;
//                 _cwait_num++;
//                 _consumer_cond.wait(_mutex);
//                 _cwait_num--;
//             }
//             *out = _q.front();
//             _q.pop();
//             //pop之后肯定有空间
//             if(_pwait_num)
//             {
//                 //唤醒productor消费者
//                 _productor_cond.Nodify();
//             }
//         }
//     private:
//         std::queue<T> _q; //将来数据就放在队列当中，未来的临界资源
//         int _cap;//阻塞队列的最大容量
//         MutexModule::Mutex _mutex;
//         CondModule::Cond _productor_cond;// 生产者条件变量
//         CondModule::Cond _consumer_cond;//  消费者条件标量
//         int _cwait_num;
//         int _pwait_num;
//     };
// }


namespace BlockQueueModule
{
    static const int gcap = 10;
    //用queue封装阻塞队列
    template<typename T>
    class BlockQueue
    {
    private:
        bool IsFull(){return _cap == _q.size();}
        bool IsEmpty(){return _q.empty();}
    public:
        //构造
        BlockQueue(int cap = gcap):_cap(cap),_cwait_num(0),_pwait_num(0)
        {
            //初始化锁
            pthread_mutex_init(&_mutex,nullptr);
            //初始化条件变量
            pthread_cond_init(&_consumer_cond,nullptr);
            pthread_cond_init(&_productor_cond,nullptr);
        }
        //析构
        ~BlockQueue()
        {
            //销毁锁
            pthread_mutex_destroy(&_mutex);

            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }
                //入队列，将数据放进队列中
        void Equeue(const T& in)
        {
            //这里不能直接push，因为当生产者push的时候，消费者可能也在pop，访问同一份资源
            //先加锁
            pthread_mutex_lock(&_mutex);
            //这里在放的时候，首先要判cap是否是满的
            //问题1：是否可以将锁占有去等待
            //结论1：在临界区中等待是必然的（目前）
            while(IsFull()) 
            {
                std::cout<<"生产者进入等待"<<std::endl;
                _pwait_num++;//等待时++
                //当数据满的时候，到指定条件变量下等待
                //pthread_cond_wait这个函数的第二个参数，是为了让我们释放这把锁
                pthread_cond_wait(&_productor_cond,&_mutex);//我们在临界区中等待是必然的！！判断队列是否为空为满本身就是在访问临界资源
                _pwait_num--;//唤醒时--
                //等待的时候必定是持有锁的，但是持有锁之后问题又来了，消费者拿不到锁了，出现死锁情况       
                //当上面这个函数返回时，有两个条件：线程被唤醒&&重新申请并持有锁,所以这个函数做了两步，进入这个函数时会释放锁，函数返回时会重新申请锁
                //为什么要重新申请锁：因为线程被唤醒的时候还是在临界区内部，所以需要被保护，所以需要重新申请锁           
            }
            //4. if条件不满足 || 线程被唤醒（队列不为满）
            _q.push(in); //生产，保证队列目前不为满

            //至少一定有一个数据----肯定有数据----还没有释放锁
            if(_cwait_num)
            {
                //唤醒consumer
                pthread_cond_signal(&_consumer_cond);//伪唤醒
            }
            pthread_mutex_unlock(&_mutex);
        }
        //取出数据
        void Pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                std::cout<<"消费者进入等待"<<std::endl;
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond,&_mutex);
                _cwait_num--;
            }
            *out = _q.front();
            _q.pop();

            //pop之后肯定有空间
            if(_pwait_num)
            {
                //唤醒productor消费者
                pthread_cond_signal(&_productor_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }
    private:
        std::queue<T> _q; //将来数据就放在队列当中，未来的临界资源
        int _cap;//阻塞队列的最大容量
        pthread_mutex_t _mutex;
        //条件变量---一个条件变量通知为空时，一个条件变量通知为满时
        pthread_cond_t _productor_cond;// 生产者条件变量
        pthread_cond_t _consumer_cond;//  消费者条件标量

        //有多少个消费者或者生产者线程去等待了
        int _cwait_num;
        int _pwait_num;
    };
}